package com.google.code.v8config;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

/**
 *
 * @author yvad
 */
public final class DataList {

    //--------------------------------------------------------------------------
    private static final String repListBegin = "^( MOXCEL.{7,7} )?+ \\ufeff \\s*+ \\{ \\s*+";
    private static final String repLexem = 
            "( \\{\\#base64:[^\\}]*+\\}"+ // Binary data
            "| \\{ "+ // Inner list begin
            "| \"(?:\"\"|[^\"])*+\""+ // String
            "| [^\\s,\\}]++"+ // Other
            ")?"+
            "\\s*+"+
            "( \\} \\s* )?+"+ // Closing brackets
            "( , \\s* )?+"; // 
    private static final int repFlags = Pattern.COMMENTS | Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE;
    private static final Pattern recpListBegin = Pattern.compile(repListBegin, repFlags);
    private static final Pattern recpLexem = Pattern.compile(repLexem, repFlags);

    //--------------------------------------------------------------------------
    public static final class Element {

        //----------------------------------------------------------------------
        public Element() {
            //
        }

        //----------------------------------------------------------------------
        private String m_String;
        private DataList m_List;
        //----------------------------------------------------------------------
        public String getString() {
            return m_String;
        }
        public void setString(String value) {
            m_String = value;
            if(m_String!=null)
                setList(null);
        }

        public DataList getList() {
            return m_List;
        }
        public void setList(DataList value) {
            m_List = value;
            if(m_List!=null)
                setString(null);
        }

        public boolean isList() {
            if(getList()!=null)
                return true;
            return false;
        }

        //----------------------------------------------------------------------
    }

    //--------------------------------------------------------------------------
    public DataList() {
        //
    }

    //--------------------------------------------------------------------------
    private String m_Prefix;
    private final List<Element> m_List = new ArrayList<Element>();

    //--------------------------------------------------------------------------
    public String getPrefix() {
        return m_Prefix;
    }
    public void setPrefix(String value) {
        m_Prefix = value;
    }

    public int getCount() {
        return m_List.size();
    }

    public Element get(int index) {
        return m_List.get(index);
    }
    public void set(int index, Element value) {
        m_List.set(index, value);
    }

    //--------------------------------------------------------------------------
    public Iterator<Element> iterator() {
        return m_List.iterator();
    }

    public void add(int index, Element value) {
        m_List.add(index, value);
    }
    public void add(Element value) {
        m_List.add(value);
    }

    public Element remove(int index) {
        return m_List.remove(index);
    }
    public boolean remove(Element value) {
        return m_List.remove(value);
    }

    public void unpack(String data) 
            throws IOException {

        Matcher remListBegin = recpListBegin.matcher(data);
        if(remListBegin.find()) {
            int position = unpackRecursively(data, remListBegin.end());
            if(position!=data.length())
                throw new IOException("Only part unpacked");
            setPrefix(remListBegin.group(1));
        } else
            throw new IOException("Data is not list");
        
    }
    public String pack() 
            throws IOException {

        StringBuilder strBuilder = new StringBuilder();

        String prefix = getPrefix();
        if(prefix!=null)
            strBuilder.append(prefix);

        strBuilder.appendCodePoint(FileUtils.DATA_PREFIX);
        strBuilder.append(packRecursively());

        return strBuilder.toString();
    }

    public Document createXML()
            throws IOException {

        Document document = null;
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            document = builder.newDocument();

            fillDOM(document, document);
        } catch(ParserConfigurationException e) {
            throw new IOException(e);
        }
        return document;
    }

    //--------------------------------------------------------------------------
    private int unpackRecursively(String data, int blockBegin)
            throws IOException {

        setPrefix(null);
        m_List.clear();

        Matcher remLexem = recpLexem.matcher(data);
        int position = blockBegin;

        while(remLexem.find(position)) {
            if(remLexem.start()!=position)
                throw new IOException("Wrong data format");

            String value = remLexem.group(1);
            String closingBracket = remLexem.group(2);
            position = remLexem.end();

            if(value==null && closingBracket!=null)
                break;

            Element element = new Element();
            if(value.equals("{")) {
                DataList list = new DataList();
                if(closingBracket==null)
                    position = ((DataList)list).unpackRecursively(data, position);
                else
                    closingBracket = null;
                element.setList(list);
            } else
                element.setString(value);
            add(element);

            if(closingBracket!=null)
                break;
        }

        return position;
    }
    private String packRecursively()
            throws IOException {

        StringBuilder strBuilder = new StringBuilder();

        strBuilder.append("{");
        for(Element element : m_List) {
            if(strBuilder.length()>1)
                strBuilder.append(",");
            if(element.isList())
                strBuilder.append(((DataList)element.getList()).packRecursively());
            else
                strBuilder.append(element.getString());
        }
        strBuilder.append("}");

        return strBuilder.toString();
    }

    private void fillDOM(Document domDocument, Node domParent)
            throws IOException {

        Node domElement = domDocument.createElement("DataList");
        domParent.appendChild(domElement);

        for(Element element : m_List)
            if(!element.isList()) {
                Node domString = domDocument.createElement("String");
                domElement.appendChild(domString);
                Node domText = domDocument.createTextNode(element.getString());
                domString.appendChild(domText);
            } else
                ((DataList)element.getList()).fillDOM(domDocument, domElement);
    }

    //--------------------------------------------------------------------------
}
